[New features] Add HySparse MQA/MLA sparse attention integration#1453
[New features] Add HySparse MQA/MLA sparse attention integration#1453ForFishes wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
该 PR 将 HySparse(块级打分 + TopK 选块 + 块稀疏 gather,arXiv:2602.03560)的算子与模型侧 MLA-absorbed MQA 注意力路径打通,并通过 enable_hy_sparse_attention 配置开关启用,用于在 full-attention 层生成共享 KV + TopK block 索引,并在 SWA 层复用以执行块稀疏注意力。
Changes:
- 新增/扩展 TileLang HySparse 相关算子(MQA/MHA block-score、MQA block-sparse、SWA wrapper)以及对应的 PyLayer 可微封装与参考实现。
- 在
MQASelfAttention/HySparseTransformerLayer/gpt_layer_specs中完成模型侧接线:full 层产出共享 KV + block 索引,SWA 层消费并叠加 sparse 分支输出。 - 增加多组算子精度/反传测试与网络集成测试(含 stop_gradient 合约、非连续梯度等)。
另外:PR 标题当前不符合仓库要求的 "[CLASS]Title" 格式;建议改为类似 [Feature] Add HySparse MQA/MLA sparse attention integration(或按项目约定选择合适的 CLASS)。
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/single_card_tests/test_mqa_self_attention.py | 新增网络级集成测试,覆盖 MLA vs MQA 对齐与跨层 KV sharing 行为 |
| tests/single_card_tests/custom_ops/test_hysparse_swa.py | 新增 SWA MQA wrapper 的前后向精度与 autograd 合约测试 |
| tests/single_card_tests/custom_ops/test_hysparse_mha_block_score.py | 新增 MHA(per-head KV)block-score 的参考对齐与 bwd 覆盖 |
| tests/single_card_tests/custom_ops/test_hysparse_differentiable_ops.py | 新增 PyLayer 包装的 stop_gradient / 非连续梯度 / 正确性测试 |
| tests/single_card_tests/custom_ops/test_hysparse_block_attn.py | 扩展 MQA 测试以覆盖 Dk!=Dv 的 absorbed-MLA 形状 |
| src/paddlefleet/transformer/transformer_layer.py | 引入 HySparseTransformerLayer,补齐 shared_kv 透传与 MTP split/restore |
| src/paddlefleet/transformer/transformer_config.py | 增加 HySparse 配置项:enable 开关、block_size、topk |
| src/paddlefleet/transformer/multi_latent_attention.py | full-attention 路径接入 FA4 fused block-score + TopK,并产出 shared_kv |
| src/paddlefleet/tilelang_ops/hysparse/swa_attn.py | 新增 SWA(滑窗)MQA 注意力 wrapper(复用 block-score kernel) |
| src/paddlefleet/tilelang_ops/hysparse/reference.py | 扩展参考实现:支持 Dv 维度、补充 MHA block-score reference、滑窗 valid_range |
| src/paddlefleet/tilelang_ops/hysparse/pipeline.py | TopK block 选择前对输入 detach,避免 TopK 进入反传导致崩溃 |
| src/paddlefleet/tilelang_ops/hysparse/differentiable_ops.py | 新增 block-score / block-sparse 的 PyLayer 可微封装与 pipeline 便捷函数 |
| src/paddlefleet/tilelang_ops/hysparse/block_sparse_attn_mqa.py | 扩展 block-sparse MQA fwd 以支持 Dk!=Dv,并加强 shape 校验 |
| src/paddlefleet/tilelang_ops/hysparse/block_sparse_attn_mqa_bwd.py | 扩展 block-sparse MQA bwd 以支持 Dk!=Dv,并调整 shared-mem 预算逻辑 |
| src/paddlefleet/tilelang_ops/hysparse/block_score_fa4.py | 新增 FA4 fused block-score wrapper(含环境探测/快速失败) |
| src/paddlefleet/tilelang_ops/hysparse/block_score_attn.py | 扩展 MQA block-score fwd 以支持 Dk!=Dv,并加强 shape 校验 |
| src/paddlefleet/tilelang_ops/hysparse/block_score_attn_mha.py | 新增 MHA(per-head KV)block-score fwd kernel 与 host 侧接口 |
| src/paddlefleet/tilelang_ops/hysparse/block_score_attn_mha_bwd.py | 新增 MHA block-score bwd kernel 与 host 侧接口 |
| src/paddlefleet/tilelang_ops/hysparse/block_score_attn_bwd.py | 扩展 MQA block-score bwd 以支持 Dk!=Dv,并调整 shared-mem 预算逻辑 |
| src/paddlefleet/tilelang_ops/hysparse/init.py | 导出新增算子/封装 API(含 SWA / FA4 wrapper) |
| src/paddlefleet/models/gpt/gpt_layer_specs.py | 启用 HySparse 时切换 attention/layer 类型到 MQASelfAttention/HySparseTransformerLayer |
| if self.config.enable_hy_sparse_attention and shared_kv is not None: | ||
| # Compressed KV latent shared with block-sparse attention in SWA | ||
| # layers (single MQA head): [B, S, 1, kv_lora_rank + qk_rope_head_dim]. | ||
| shared_key = paddle.concat( | ||
| [kv_compressed.unsqueeze(2), k_pos_emb], axis=-1 | ||
| ) | ||
| shared_kv.append(shared_key) | ||
| # block_indices produced by the MHA block-score path above. | ||
| shared_kv.append(block_indices) |
| ctx.save_for_backward(q, k, v, startend_row_indices, out, lse) | ||
| ctx.sm_scale = sm_scale | ||
| ctx.causal = causal | ||
| return out, lse | ||
|
|
||
| @staticmethod | ||
| def backward(ctx, dout, *_): | ||
| from paddlefleet_ops.flash_mask.cute.flashmask_utils import ( | ||
| FlashMaskInfoPaddle, | ||
| ) | ||
| from paddlefleet_ops.flash_mask.cute.interface import _flash_attn_bwd | ||
|
|
||
| q, k, v, startend_row_indices, out, lse = ctx.saved_tensor() | ||
| flashmask_info = None | ||
| if startend_row_indices is not None: | ||
| flashmask_info = FlashMaskInfoPaddle( | ||
| startend_row_indices=startend_row_indices, | ||
| is_causal=ctx.causal, | ||
| ) |
| # Grads for the tensor inputs that require grad: q, k, v only. | ||
| return dq, dk, dv |
PaddleFleet Log Analysis
日志分析报告
失败的测试case: 根本原因分析:
Coverage 不达标:本 PR 新增了大量 HySparse/MLA 代码( 修复建议:
🔄 每次 Re-run 后自动更新 |
risemeup1111
left a comment
There was a problem hiding this comment.
已复查当前提交,代码层面仍有会导致导入或训练反向执行失败的阻塞问题,具体请看 inline comments。
优先级:P3 非行级:PR 描述模板字段不属于 diff 行,无法挂 inline。当前
Check PR Description仍在报PR Category/PR Types不符合允许枚举,建议按仓库模板在描述开头补充,例如### PR Category使用Performance Optimization,### PR Types使用New features,再保留现有 What/Tests 内容。处理要求:请针对该评论进行回复(同意并已修改请回复 Done,不同意请说明理由)。
| hysparse_forward_mqa, | ||
| select_topk_blocks, | ||
| ) | ||
| from .block_score_fa4 import block_score_fa4_attn_fwd |
There was a problem hiding this comment.
优先级:P1
这里在包初始化阶段直接导入 block_score_fa4,会把 paddlefleet_ops.flash_mask.cute.* 的 SM10/Blackwell 依赖提前带入。paddlefleet_ops.__init__ 里只有 is_flash_mask_available() 为 true 时才加载 flash_mask,否则会通过 HardwareIncompatibleBlocker 阻断 paddlefleet_ops.flash_mask 导入;因此在 H20/A100 或非 Blackwell 环境中,只要代码执行 from paddlefleet.tilelang_ops.hysparse import block_sparse_mqa_attention / sliding_window_mqa_attention,即使不走 FA4 full layer,也会在导入阶段失败。
处理要求:请针对该评论修复并提交新的 commit。
建议让 hysparse 包级入口保持非 FA4 路径可导入,把 FA4-only 的 cute import 延迟到实际调用并加可用性检查,例如:
# hysparse/__init__.py: 不要在包初始化时 eager import block_score_fa4
from .differentiable_ops import block_sparse_mqa_attention
from .pipeline import select_topk_blocks
from .swa_attn import sliding_window_mqa_attention
def block_score_fa4_attn_fwd(*args, **kwargs):
from .block_score_fa4 import block_score_fa4_attn_fwd as _impl
return _impl(*args, **kwargs)同时在 block_score_fa4.py 内部用 paddlefleet_ops.is_flash_mask_available() 做明确报错/跳过,或把 FlashMaskInfoPaddle、_flash_attn_fwd/_bwd 的导入移动到 guarded 的执行路径中。
There was a problem hiding this comment.
优先级:P1
新提交 99e25e6 又把这个问题带回来了:hysparse/__init__.py 仍执行 from .block_score_fa4 import block_score_fa4_attn_fwd,而当前 block_score_fa4.py 在模块顶层导入 paddlefleet_ops.flash_mask.cute.flashmask_utils/interface。相比上一版,_import_fa4() / is_flash_mask_available() 懒加载也被删除,所以非 SM100 或无 flash_mask 的环境会在导入 paddlefleet.tilelang_ops.hysparse 时先失败,连只使用 sliding_window_mqa_attention 的路径也无法加载。
处理要求:请针对该评论修复并提交新的 commit。
请恢复 package 级入口的懒加载/可用性检查,例如:
# hysparse/__init__.py 不要在模块初始化时导入 FA4-only 依赖
def block_score_fa4_attn_fwd(*args, **kwargs):
from .block_score_fa4 import block_score_fa4_attn_fwd as _impl
return _impl(*args, **kwargs)| ], | ||
| ) | ||
| # Grads for the tensor inputs that require grad: q, k, v only. | ||
| return dq, dk, dv |
There was a problem hiding this comment.
优先级:P1
_BlockScoreFA4Attn.forward() 的 Tensor 输入包含 q/k/v/block_logit/startend_row_indices,但 backward 这里只返回了 dq/dk/dv。Paddle 的 PyLayer 会按前向 Tensor 输入位置映射 backward 返回值,少了 block_logit 和 startend_row_indices 的 None 槽位会导致反向执行契约不匹配;另外当前 ctx.save_for_backward(q, k, v, startend_row_indices, out, lse) 在 startend_row_indices=None(该 API 默认允许)时也会把非 Tensor 存入 saved tensors。
处理要求:请针对该评论修复并提交新的 commit。
建议同时修复保存逻辑和返回槽位,形状如下:
ctx.has_mask = startend_row_indices is not None
if ctx.has_mask:
ctx.save_for_backward(q, k, v, startend_row_indices, out, lse)
else:
ctx.save_for_backward(q, k, v, out, lse)
ctx.needs_grad = (
not q.stop_gradient,
not k.stop_gradient,
not v.stop_gradient,
)
ctx.mark_non_differentiable(lse)
...
if ctx.has_mask:
q, k, v, startend_row_indices, out, lse = ctx.saved_tensor()
else:
q, k, v, out, lse = ctx.saved_tensor()
startend_row_indices = None
...
gq, gk, gv = ctx.needs_grad
return (
dq if gq else None,
dk if gk else None,
dv if gv else None,
None, # block_logit
None, # startend_row_indices
)There was a problem hiding this comment.
优先级:P1
新提交 99e25e6 回退了这个修复,而且当前 forward 还新增了 block_bos 这个 Tensor 输入:ctx.save_for_backward(q, k, v, startend_row_indices, out, lse) 仍会在 startend_row_indices is None 时保存非 Tensor,backward 仍只 return dq, dk, dv。当前 Tensor 输入至少包含 q/k/v/block_logit/block_bos,有 mask 时还包含 startend_row_indices,因此反向返回槽位还缺 block_logit、block_bos(以及 mask Tensor 对应的 None)。
处理要求:请针对该评论修复并提交新的 commit。
建议恢复“只保存 Tensor + 显式 None 槽位”的形状,并把新增的 block_bos 也纳入返回契约:
ctx.save_for_backward(q, k, v, out, lse)
ctx.startend_row_indices = startend_row_indices
ctx.has_startend = startend_row_indices is not None
ctx.needs_grad = (
not q.stop_gradient,
not k.stop_gradient,
not v.stop_gradient,
)
...
gq, gk, gv = ctx.needs_grad
grads = [
dq if gq else None,
dk if gk else None,
dv if gv else None,
None, # block_logit
None, # block_bos
]
if ctx.has_startend:
grads.append(None) # startend_row_indices
return tuple(grads)| ) | ||
| shared_kv.append(shared_key) | ||
| # block_indices produced by the MHA block-score path above. | ||
| shared_kv.append(block_indices) |
There was a problem hiding this comment.
优先级:P1
这里无条件追加 block_indices,但 block_indices 只在上面的 HySparse full-attention 分支里赋值。当前分支顺序是先判断 self.recompute_core_attention and self.training,所以当训练时同时打开 enable_hy_sparse_attention=True 和 recompute_core_attention=True 时,会先走 recompute 的普通 core_attention 分支,然后执行到这里触发 UnboundLocalError。这个配置组合是现有 MLA attention 的可用训练开关,不能因为 HySparse 打开后直接崩溃。
处理要求:请针对该评论修复并提交新的 commit。
建议把 HySparse full-attention 分支放在 recompute 分支之前,或显式禁止这个组合。若选择保留 HySparse,可按下面形状调整:
if self.config.enable_hy_sparse_attention and shared_kv is not None:
core_attn_out, block_indices = self._hy_sparse_full_attention(
query,
key,
value,
attn_mask_startend_row_indices,
)
elif self.recompute_core_attention and self.training:
core_attn_out = recompute(
self.core_attention,
...
)
else:
core_attn_out = self.core_attention(...)如果当前 FA4 full path 暂不支持该 recompute 模式,也请在进入这里前抛出明确的 ValueError,避免后续引用未定义变量。
risemeup1111
left a comment
There was a problem hiding this comment.
已复查新提交。上一轮提出的 3 个 P1 代码问题在当前 head 中已看到对应修复;本轮新增发现一个 windowed MQA backward 尾块越界读风险,详情已放在 inline comment。该问题需要修复后再继续复查。
| block_N=block_N, | ||
| block_B=block_B, | ||
| ) | ||
| dq = bwd(q, k, v, do, lse, delta, valid_range, block_range, dk, dv) |
There was a problem hiding this comment.
优先级:P1
这里把原始未 padding 的 q / do 直接传入 windowed_mqa_bwd,但 kernel 网格是 T.ceildiv(seq_len, BM),最后一个 query tile 会执行 T.copy(Q[bb, bm * BM : (bm + 1) * BM, ...]) 和 T.copy(dO[...])。当 seq_len % block_M != 0 时,例如 s=130 且 auto-fit 选到 block_M=64,最后一个 tile 会从 [128, 192) 读 Q/dO,超过真实 seq_len=130;当前只给 bos/eos 的 BlockRange 做了 padding,写回 dQ 虽有 guard,但越界读已经发生,训练反向会有 illegal memory access 或未定义结果风险。现有 test_hysparse_windowed_swa.py 的 backward case 使用 s=128,没有覆盖这个尾块。
处理要求:请针对该评论修复并提交新的 commit。
建议在 kernel 内按行 guard 加载 Q/dO(或在 host 侧同步 padding q/do/lse/delta 后再 trim dq)。直接在 kernel 里改的形状如下:
for i, d in T.Parallel(BM, D):
row = bm * BM + i
in_range = row < seq_len
safe_row = T.if_then_else(in_range, row, 0)
Q_shared[i, d] = T.if_then_else(
in_range, Q[bb, safe_row, bh, d], T.cast(0, dtype)
)
for i, d in T.Parallel(BM, D_v):
row = bm * BM + i
in_range = row < seq_len
safe_row = T.if_then_else(in_range, row, 0)
dO_shared[i, d] = T.if_then_else(
in_range, dO[bb, safe_row, bh, d], T.cast(0, dtype)
)同时补一个 s % block_M != 0 的 sliding_window_mqa_attention backward 测试,例如 s=130 或显式 block_M=64 的路径,防止尾块回归。
risemeup1111
left a comment
There was a problem hiding this comment.
已复查最新提交。当前仍有阻塞问题:新提交在 transformer_layer.py 引入了 Gemma4、act offload 和 MoE 子类兼容性回归,详情见 inline comments;同时此前两个 FA4 P1 修复被回退,已在原线程补充。之前关于 HySparse recompute 顺序和 windowed backward 尾块读取的 P1 线程在当前代码中也仍未解决,沿用原线程不重复开新评论。请修复并提交新的 commit 后再复查。
| if isinstance(sublayers_spec.mlp.layer, type) and issubclass( | ||
| sublayers_spec.mlp.layer, MoELayer | ||
| ): | ||
| if sublayers_spec.mlp.layer == MoELayer: |
There was a problem hiding this comment.
优先级:P1
这里把 MoE 判断从 issubclass(..., MoELayer) 收窄成了精确相等,导致现有 Gemma4MoELayer(MoELayer) 走到 unknown MLP 分支,additional_mlp_kwargs 不再传 pg_collection。gpt_layer_specs.py 的 attention_layer_type == "gemma4" 当前就是 LayerSpec(layer=Gemma4MoELayer, ...),而 Gemma4MoELayer.__init__ 会把 pg_collection 继续传给基类和 router;不传会破坏 Gemma4 MoE 构建/并行配置。
处理要求:请针对该评论修复并提交新的 commit。
建议恢复子类判断:
| if sublayers_spec.mlp.layer == MoELayer: | |
| if isinstance(sublayers_spec.mlp.layer, type) and issubclass( | |
| sublayers_spec.mlp.layer, MoELayer | |
| ): |
There was a problem hiding this comment.
已在当前 head 复查,MoE 判断已恢复为 isinstance(..., type) + issubclass(..., MoELayer),Gemma4MoELayer 这类 MoELayer 子类会继续拿到 pg_collection。这条问题我视为已修复。
| ) | ||
| return offload_kwargs | ||
|
|
||
| def build_schedule_node(self): |
There was a problem hiding this comment.
优先级:P1
这个提交删除了 TransformerLayer._compute_act_offload_kwargs(),但仓库现有 tests/single_card_tests/ai_edited_test/transformer/test_ai_transformer_layer.py::TestDecoderlayerActOffloadSettings 仍直接调用 TransformerLayer._compute_act_offload_kwargs(fake);当前 head 会变成 AttributeError。同时 base forward() 里也移除了 offload_kwargs = self._compute_act_offload_kwargs() 和 recompute(..., **offload_kwargs),会让 decoderlayer_act_offload_settings 在 full/recovery recompute 路径上静默失效。
处理要求:请针对该评论修复并提交新的 commit。
这里需要同时恢复方法和 recompute 透传,形状如下:
def _compute_act_offload_kwargs(self):
decoderlayer_act_offload_settings = self.config.get(
"decoderlayer_act_offload_settings", {"type": "", "value": ""}
) or {"type": "", "value": ""}
...
return offload_kwargs
...
offload_kwargs = self._compute_act_offload_kwargs()
outputs = recompute(
self._forward_impl,
...,
**offload_kwargs,
)| if mtp_tmp_dict is not None: | ||
| rst = {**rst, **mtp_tmp_dict} | ||
| output_grad = output_grad + tuple(mtp_tmp_grad) | ||
| return rst, output_grad |
There was a problem hiding this comment.
优先级:P1
这个 hunk 删除了 Gemma4TransformerLayerSublayersSpec 和 Gemma4TransformerLayer,但当前代码仍然引用它们:src/paddlefleet/models/gpt/gemma4_layer_specs.py 直接 from paddlefleet.transformer.transformer_layer import Gemma4TransformerLayer,gpt_layer_specs.py 在 attention_layer_type == "gemma4" 路径导入 Gemma4TransformerLayerSublayersSpec,tests/single_card_tests/transformer/test_gemma4.py 也覆盖这些符号。当前 head 下 Gemma4 layer spec 构建会在导入阶段失败。
处理要求:请针对该评论修复并提交新的 commit。
请恢复这些定义,或把所有引用和测试同步迁移到新的定义位置;需要保留的接口形状至少包括:
@dataclass
class Gemma4TransformerLayerSublayersSpec(TransformerLayerSublayersSpec):
post_self_attn_layernorm: LayerSpec | type = IdentityOp
pre_mlp_layernorm: LayerSpec | type = IdentityOp
post_mlp_layernorm: LayerSpec | type = IdentityOp
class Gemma4TransformerLayer(TransformerLayer):
def __init__(...):
...
def _forward_impl(...):
...There was a problem hiding this comment.
已在当前 head 复查,Gemma4TransformerLayerSublayersSpec 和 Gemma4TransformerLayer 已恢复到 transformer_layer.py,gemma4_layer_specs.py / gpt_layer_specs.py 的导入路径也重新可用。这条问题我视为已修复。
| hysparse_forward_mqa, | ||
| select_topk_blocks, | ||
| ) | ||
| from .block_score_fa4 import block_score_fa4_attn_fwd |
There was a problem hiding this comment.
优先级:P1
新提交 99e25e6 又把这个问题带回来了:hysparse/__init__.py 仍执行 from .block_score_fa4 import block_score_fa4_attn_fwd,而当前 block_score_fa4.py 在模块顶层导入 paddlefleet_ops.flash_mask.cute.flashmask_utils/interface。相比上一版,_import_fa4() / is_flash_mask_available() 懒加载也被删除,所以非 SM100 或无 flash_mask 的环境会在导入 paddlefleet.tilelang_ops.hysparse 时先失败,连只使用 sliding_window_mqa_attention 的路径也无法加载。
处理要求:请针对该评论修复并提交新的 commit。
请恢复 package 级入口的懒加载/可用性检查,例如:
# hysparse/__init__.py 不要在模块初始化时导入 FA4-only 依赖
def block_score_fa4_attn_fwd(*args, **kwargs):
from .block_score_fa4 import block_score_fa4_attn_fwd as _impl
return _impl(*args, **kwargs)| ], | ||
| ) | ||
| # Grads for the tensor inputs that require grad: q, k, v only. | ||
| return dq, dk, dv |
There was a problem hiding this comment.
优先级:P1
新提交 99e25e6 回退了这个修复,而且当前 forward 还新增了 block_bos 这个 Tensor 输入:ctx.save_for_backward(q, k, v, startend_row_indices, out, lse) 仍会在 startend_row_indices is None 时保存非 Tensor,backward 仍只 return dq, dk, dv。当前 Tensor 输入至少包含 q/k/v/block_logit/block_bos,有 mask 时还包含 startend_row_indices,因此反向返回槽位还缺 block_logit、block_bos(以及 mask Tensor 对应的 None)。
处理要求:请针对该评论修复并提交新的 commit。
建议恢复“只保存 Tensor + 显式 None 槽位”的形状,并把新增的 block_bos 也纳入返回契约:
ctx.save_for_backward(q, k, v, out, lse)
ctx.startend_row_indices = startend_row_indices
ctx.has_startend = startend_row_indices is not None
ctx.needs_grad = (
not q.stop_gradient,
not k.stop_gradient,
not v.stop_gradient,
)
...
gq, gk, gv = ctx.needs_grad
grads = [
dq if gq else None,
dk if gk else None,
dv if gv else None,
None, # block_logit
None, # block_bos
]
if ctx.has_startend:
grads.append(None) # startend_row_indices
return tuple(grads)Bring in upstream/develop's activation-offload / origin_input_ids threading in TransformerLayer (resolving the transformer_layer.py conflict) and restore the _compute_act_offload_kwargs method, then apply it to HySparseTransformerLayer's recompute path (recompute inside the recovery window, offload_kwargs threaded). Review-comment fixes: - hysparse/__init__.py: lazy-import block_score_fa4 (FA4 cute is SM10-only) so swa/select_topk import on non-Blackwell hardware. - block_score_fa4.py: PyLayer backward returns one grad slot per forward Tensor input (None for block_logit/block_bos/startend_row_indices), guards the optional startend_row_indices save, marks lse non-differentiable. - windowed_mqa_attn_bwd.py: guarded per-row Q/dO load to avoid OOB when seq_len % block_M != 0. - multi_latent_attention.py: check the HySparse full-attention branch before recompute_core_attention so block_indices is always produced. - gpt_layer_specs.py: reject enable_hy_sparse_attention combined with enable_hyper_connections / block_attention_residuals. - test_hysparse_windowed_swa.py: add ragged seqlen (S=130) backward regression test for the windowed-bwd OOB guard.
|
Rebased onto latest
Verified locally on B30Z (sm100): |
risemeup1111
left a comment
There was a problem hiding this comment.
已复查新提交。此前关于 FA4 懒加载、FA4 PyLayer 返回槽位、HySparse full-attention/recompute 顺序、windowed backward 尾块越界读、activation offload 透传的阻塞问题看起来已经修复。
仍有阻塞项需要继续处理:此前关于 MoE 子类判断和 Gemma4 Transformer layer 定义的行级线程仍未修复,请继续按原线程提交修复;另外我新增了一条行级评论,enable_mtp_magic_send 的 MTP embedding spec 被删除后会导致 MTP forward 缺少 mtp_input_embeds。
| if config.enable_hyper_connections | ||
| else None, | ||
| tail_empty_layers=tail_empty_layers_spec, | ||
| mtp=mtp_layers_spec, |
There was a problem hiding this comment.
新提交删除了 MTPEmbeddingLayer 的 spec 生成和 mtp_embedding=mtp_embedding_spec 注入。当前 GPTSublayersSpec 仍有 mtp_embedding 字段,GPTModel.get_layer_desc_list() 只有在 spec.mtp_embedding 存在时才会插入 MTPEmbeddingLayer;而 MultiTokenPredictionLayer.forward() 在 enable_mtp_magic_send=True 时会要求 dict_args["mtp_input_embeds"],否则直接抛出 RuntimeError("MTPEmbeddingLayer may not have been executed.")。因此 enable_mtp_magic_send=True && num_nextn_predict_layers > 0 的 GPT spec 会构建出没有 MTP re-embedding 层的 pipeline,MTP forward 会失败。
处理要求:请针对该评论修复并提交新的 commit。
请恢复 magic-send 的 embedding spec 和注入,形状如下:
from paddlefleet.models.gpt.mtp_embedding_layer import MTPEmbeddingLayer
...
mtp_embedding_spec = None
if config.enable_mtp_magic_send and config.num_nextn_predict_layers > 0:
mtp_embedding_spec = LayerSpec(
layer=MTPEmbeddingLayer,
extra_kwargs={"config": config},
)
...
GPTSublayersSpec(
...
mtp=mtp_layers_spec,
mtp_embedding=mtp_embedding_spec,
mtp_lm_head=mtp_lm_head_spec,
...
)There was a problem hiding this comment.
已在当前 head 复查,MTPEmbeddingLayer 的 import、mtp_embedding_spec 构造以及 GPTSublayersSpec 的 mtp_embedding 注入都已恢复,这条问题我视为已修复。
…-count guard, test skip - transformer_layer HySparse SWA path: raise a clear ValueError with layer_number when a SWA layer receives no shared KV/block-index state from a preceding full-attention layer, instead of passing [None, None] and crashing later in MQASelfAttention with an opaque AttributeError. - block_sparse_mqa_dsa: reject H > 64 query heads up front with a clear ValueError (FlashMLA sparse fwd fixes h_q at 64 and only supports zero-padding H <= 64), instead of failing deep in the CUDA op on a mismatched h_q. - test_mqa_self_attention.test_kv_sharing: skip when the FA4 FlashMask / cuDNN DSA backends are unavailable (non-Blackwell / CPU-only), so the test no longer errors at import/op-launch time on unsupported hardware.
Bring in upstream/develop's activation-offload / origin_input_ids threading in TransformerLayer (resolving the transformer_layer.py conflict) and restore the _compute_act_offload_kwargs method, then apply it to HySparseTransformerLayer's recompute path (recompute inside the recovery window, offload_kwargs threaded). Review-comment fixes: - hysparse/__init__.py: lazy-import block_score_fa4 (FA4 cute is SM10-only) so swa/select_topk import on non-Blackwell hardware. - block_score_fa4.py: PyLayer backward returns one grad slot per forward Tensor input (None for block_logit/block_bos/startend_row_indices), guards the optional startend_row_indices save, marks lse non-differentiable. - windowed_mqa_attn_bwd.py: guarded per-row Q/dO load to avoid OOB when seq_len % block_M != 0. - multi_latent_attention.py: check the HySparse full-attention branch before recompute_core_attention so block_indices is always produced. - gpt_layer_specs.py: reject enable_hy_sparse_attention combined with enable_hyper_connections / block_attention_residuals. - test_hysparse_windowed_swa.py: add ragged seqlen (S=130) backward regression test for the windowed-bwd OOB guard.
…-count guard, test skip - transformer_layer HySparse SWA path: raise a clear ValueError with layer_number when a SWA layer receives no shared KV/block-index state from a preceding full-attention layer, instead of passing [None, None] and crashing later in MQASelfAttention with an opaque AttributeError. - block_sparse_mqa_dsa: reject H > 64 query heads up front with a clear ValueError (FlashMLA sparse fwd fixes h_q at 64 and only supports zero-padding H <= 64), instead of failing deep in the CUDA op on a mismatched h_q. - test_mqa_self_attention.test_kv_sharing: skip when the FA4 FlashMask / cuDNN DSA backends are unavailable (non-Blackwell / CPU-only), so the test no longer errors at import/op-launch time on unsupported hardware.
risemeup1111
left a comment
There was a problem hiding this comment.
已复查当前提交,Nyanpasu 此前提出的阻塞问题均已在当前 head 中修复,未发现新的需要阻塞合入的问题。
当前 Build Fleet whl 失败点是拉取构建镜像超时,不是代码编译错误;后续可重跑 CI 确认。
Bring in upstream/develop's activation-offload / origin_input_ids threading in TransformerLayer (resolving the transformer_layer.py conflict) and restore the _compute_act_offload_kwargs method, then apply it to HySparseTransformerLayer's recompute path (recompute inside the recovery window, offload_kwargs threaded). Review-comment fixes: - hysparse/__init__.py: lazy-import block_score_fa4 (FA4 cute is SM10-only) so swa/select_topk import on non-Blackwell hardware. - block_score_fa4.py: PyLayer backward returns one grad slot per forward Tensor input (None for block_logit/block_bos/startend_row_indices), guards the optional startend_row_indices save, marks lse non-differentiable. - windowed_mqa_attn_bwd.py: guarded per-row Q/dO load to avoid OOB when seq_len % block_M != 0. - multi_latent_attention.py: check the HySparse full-attention branch before recompute_core_attention so block_indices is always produced. - gpt_layer_specs.py: reject enable_hy_sparse_attention combined with enable_hyper_connections / block_attention_residuals. - test_hysparse_windowed_swa.py: add ragged seqlen (S=130) backward regression test for the windowed-bwd OOB guard.
…-count guard, test skip - transformer_layer HySparse SWA path: raise a clear ValueError with layer_number when a SWA layer receives no shared KV/block-index state from a preceding full-attention layer, instead of passing [None, None] and crashing later in MQASelfAttention with an opaque AttributeError. - block_sparse_mqa_dsa: reject H > 64 query heads up front with a clear ValueError (FlashMLA sparse fwd fixes h_q at 64 and only supports zero-padding H <= 64), instead of failing deep in the CUDA op on a mismatched h_q. - test_mqa_self_attention.test_kv_sharing: skip when the FA4 FlashMask / cuDNN DSA backends are unavailable (non-Blackwell / CPU-only), so the test no longer errors at import/op-launch time on unsupported hardware.
risemeup1111
left a comment
There was a problem hiding this comment.
已复查当前提交,GPT/MTP spec 相关的上一个阻塞点在当前 head 中已不再复现;但仍有一处需要修复后再合入的问题,细节见 inline comment。当前主要测试还在运行,建议后续结合 CI 结果继续确认。
| ) | ||
| self._log_md5(hidden_states, "post_attn_residual", self.layer_number) | ||
| with profile(timer_name): | ||
| output = self._forward_mlp(hidden_states, input_ids=input_ids) |
There was a problem hiding this comment.
优先级:P1
HySparseTransformerLayer 这里仍只把 input_ids 传给 _forward_mlp,没有沿用基类的 origin_input_ids 透传。MoELayer.forward / TopKRouter 在 gpt_model_use_experimental_version=True 时会用 origin_input_ids 计算 aux/z loss 的原始 token mask;当前 HySparse + MoE + experimental dataflow 下会退回截断后的 input_ids,padding/MTP token 的分母会错。
处理要求:请针对该评论修复并提交新的 commit。
请和基类保持一致,在 recompute 参数、_forward_impl 签名和 _forward_mlp 调用中都传递该参数,形状如下:
outputs = recompute(
self._forward_impl,
...,
input_ids=dict_args.get("input_ids", None),
origin_input_ids=dict_args.get("origin_input_ids", None),
shared_key=dict_args.get("shared_key", None),
...,
)
def _forward_impl(..., input_ids=None, origin_input_ids=None, ...):
...
output = self._forward_mlp(
hidden_states,
input_ids=input_ids,
origin_input_ids=origin_input_ids,
)…arse DSA gather) - FA4-fused block scoring (block_score_fa4) with PyLayer grads - SWA sliding-window MQA (swa_attn) replacing standalone block_sparse_attn_mqa - block-sparse DSA gather via cuDNN (block_sparse_mqa_dsa) with d_v<512 value-region padding to satisfy FlashMLA sparse kernel (d_v==512) and learnable per-head attn_sink - MLA/transformer wiring in multi_latent_attention, transformer_layer, gpt_layer_specs; enable_hy_sparse_attention config plumbing - runtime guards: fusion_layer_utils fp8-scales ImportError fallback for older paddlefleet_ops; transformer_config MTP-must-be-full-attention check
- FA4 block-score grad / topk-consistency / multidoc tests - MQA gather DSA, windowed SWA, pipeline, whole-flow precision tests - MQA self-attention module test, build_hysparse_valid_range test - remove obsolete block-attn test superseded by the above
…aise on unsupported CP/TP - HySparseTransformerLayer.forward / _forward_impl now accept and forward origin_input_ids to _forward_mlp (recompute + non-recompute paths), matching the base TransformerLayer. Fixes wrong aux/z-loss token denominator under HySparse + MoE + gpt_model_use_experimental_version=True (P1 review comment). - multi_latent_attention MQA: replace CP/TP-unsupported asserts with explicit ValueError so the guard survives python -O (review suggestion).
|
已修复并提交 commit P1(risemeup1111 / bot,
🟡 建议(bot, if get_context_parallel_world_size() > 1:
raise ValueError("MQA does not support context parallel.")
if get_pg_size(self.pg_collection.tp) != 1:
raise ValueError("MQA does not support tensor parallel.")另:关于「变更量较大建议拆分」——本 PR 已把历史噪声提交整理为语义提交(HySparse 实现 / 测试 / 本次 review 修复),且所有改动均 gated 在 |
risemeup1111
left a comment
There was a problem hiding this comment.
已复查新提交。此前关于 HySparseTransformerLayer 未透传 origin_input_ids 的 P1 问题已修复:recompute 参数、_forward_impl 签名和 _forward_mlp 调用现在都与基类保持一致;MQA 的 CP/TP 不支持校验也已改为显式 ValueError。
本轮没有新增 inline comment,未发现需要阻塞合入的问题。
PaddlePaddle-bot
left a comment
There was a problem hiding this comment.
🤖 Paddle-CI-Agent | pr_review | 2026-07-15 23:14:00 Asia/Shanghai
📋 Review 摘要
本轮按风险优先审查了配置入口、GPT layer spec、HySparseTransformerLayer 状态传递、MQA/SWA/FA4/DSA PyLayer 边界和新增测试;未全量覆盖所有 TileLang kernel 细节。
🔎 发现的问题
| 严重级别 | 位置 | 问题 | 建议 |
|---|---|---|---|
| 🟡 建议 | src/paddlefleet/transformer/multi_latent_attention.py:1880 |
HySparse MQA 生产前向路径仍用 assert 做运行时配置校验,python -O 下会被移除。 |
改为显式 ValueError/TypeError,与 CP/TP 校验保持一致。 |
| 🟡 建议 | tests/single_card_tests/test_mqa_self_attention.py:128 |
test_forward_backward 使用 layer_number=0,实际走 MLA fallback,未覆盖新增 MQA/SWA 主路径。 |
将真实 MQA 回归构造成 SWA 层并提供 shared_kv/backend skip,或拆成 fallback 用例 + 真 MQA 用例。 |
| 🟡 建议 | PR 整体 | PR 规模较大,kernel、wiring、测试混在一次变更中。 | 后续同类变更建议拆分,便于审查和 CI 失败归因。 |
📝 PR 规范检查
已修复:标题包含官方 Tag,描述也已补齐 PR Category / PR Types / Description 结构。
🧾 历史 Findings 状态
| id | 状态 | 证据 |
|---|---|---|
f_312c3b1c |
resolved | HySparseTransformerLayer._forward_impl 当前调用 _forward_mlp(..., origin_input_ids=origin_input_ids),已保留 MTP 需要的入参。 |
f_6065473b |
resolved | MQASelfAttention.forward 当前对 CP/TP 不支持配置使用显式 ValueError。 |
f_0f9d9c79 |
still_unresolved | build_hysparse_valid_range 中 flashmask layout 校验仍是 assert。 |
| rotary_pos_cos = None | ||
| rotary_pos_sin = None | ||
|
|
||
| assert self.config.rope_type == "rope", ( |
There was a problem hiding this comment.
🟡 建议 这里是 HySparse MQA 生产前向路径的配置校验,不能依赖 assert。python -O 会移除该检查,错误的 rope_type 或 packed_seq_params 会继续进入 RoPE/TileLang/DSA kernel,最终表现为难定位的 shape/kernel 错误。建议改成显式 if ...: raise ValueError(...),和上面的 CP/TP ValueError 保持一致。
| mqa = MQASelfAttention( | ||
| config, | ||
| self.sublayer_spec, | ||
| layer_number=0, |
There was a problem hiding this comment.
🟡 建议 这个测试名和断言看起来想覆盖 MQA forward/backward,但 layer_number=0 + window_attn_skip_freq=2 会让 self.is_swa=False,因此 MQASelfAttention.is_mqa=False,第 1658 行直接走 super().forward() 的 MLA fallback。也就是说当前测试没有覆盖新增 SWA MQA 主路径、sliding_window_mqa_attention、shared_kv 消费或 sparse branch。建议把真正的 MQA 回归构造成 SWA 层(如 layer_number=1)并提供 shared_kv/backend skip,或把此用例改名为 fallback 对齐并另补真实 MQA 用例。
PR Category
Performance Optimization
PR Types
New features
Description
Integrate HySparse block-sparse attention (arXiv:2602.03560), MLA-absorbed MQA
variant, into the model:
tilelang_ops/hysparse): MQA/MHA block-score, MQA block-sparsegather, sliding-window MQA, and the FA4-fused full block-score kernel
(
block_score_fa4), withpaddle.autograd.PyLayerdifferentiable wrappersand a dense reference.
MQASelfAttention/HySparseTransformerLayer/gpt_layer_specsconnect the full-attention layer (produces shared compressed KV + top-k block
indices) to the SWA layer (consumes them for the block-sparse branch), gated
by
enable_hy_sparse_attention.tests.
Review fixes in this update:
is_flash_mask_available(); save onlytensors, stash the optional mask on
ctx, marklsenon-differentiable, andreturn
Nonegrad slots forblock_logit/startend_row_indices.UnboundLocalErrorofblock_indices).top-k so packed multi-document selection aligns with the sparse gather.
ValueErrorwhen a SWA layer runs without a preceding full-attentionlayer's shared state; require MLA attention when enabling HySparse.
unavailable (e.g. Hopper H20 CI runners).